* @parent: (allow-none): ASCII SHA256 checksum for parent, or %NULL for none
* @subject: Subject
* @body: (allow-none): Body
+ * @metadata: (allow-none): GVariant of type a{sv}, or %NULL for none
* @root_contents_checksum: ASCII SHA256 checksum for %OSTREE_OBJECT_TYPE_DIR_TREE
* @root_metadata_checksum: ASCII SHA256 checksum for %OSTREE_OBJECT_TYPE_DIR_META
* @out_commit: (out): Resulting ASCII SHA256 checksum for commit
const char *parent,
const char *subject,
const char *body,
+ GVariant *metadata,
const char *root_contents_checksum,
const char *root_metadata_checksum,
char **out_commit,
now = g_date_time_new_now_utc ();
commit = g_variant_new ("(@a{sv}@ay@a(say)sst@ay@ay)",
- create_empty_gvariant_dict (),
+ metadata ? metadata : create_empty_gvariant_dict (),
parent ? ostree_checksum_to_bytes_v (parent) : ot_gvariant_new_bytearray (NULL, 0),
g_variant_new_array (G_VARIANT_TYPE ("(say)"), NULL, 0),
subject, body ? body : "",
return ret;
}
+GFile *
+_ostree_repo_get_commit_metadata_loose_path (OstreeRepo *self,
+ const char *checksum)
+{
+ gs_free char *commit_path = ostree_get_relative_object_path (checksum, OSTREE_OBJECT_TYPE_COMMIT, FALSE);
+ return ot_gfile_resolve_path_printf (self->repodir, "%smeta", commit_path);
+}
+
+/**
+ * ostree_repo_read_commit_detached_metadata:
+ * @self: Repo
+ * @checksum: ASCII SHA256 commit checksum
+ * @out_metadata: (out) (transfer full): Metadata associated with commit in with format "a{sv}", or %NULL if none exists
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * OSTree commits can have arbitrary metadata associated; this
+ * function retrieves them. If none exists, @out_metadata will be set
+ * to %NULL.
+ */
+gboolean
+ostree_repo_read_commit_detached_metadata (OstreeRepo *self,
+ const char *checksum,
+ GVariant **out_metadata,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gs_unref_object GFile *metadata_path =
+ _ostree_repo_get_commit_metadata_loose_path (self, checksum);
+ gs_unref_variant GVariant *ret_metadata = NULL;
+ GError *temp_error = NULL;
+
+ if (!ot_util_variant_map (metadata_path, G_VARIANT_TYPE ("a{sv}"),
+ TRUE, &ret_metadata, &temp_error))
+ {
+ if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ ot_transfer_out_value (out_metadata, &ret_metadata);
+ out:
+ return ret;
+}
+
+/**
+ * ostree_repo_write_commit_detached_metadata:
+ * @self: Repo
+ * @checksum: ASCII SHA256 commit checksum
+ * @metadata: (allow-none): Metadata to associate with commit in with format "a{sv}", or %NULL to delete
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Replace any existing metadata associated with commit referred to by
+ * @checksum with @metadata. If @metadata is %NULL, then existing
+ * data will be deleted.
+ */
+gboolean
+ostree_repo_write_commit_detached_metadata (OstreeRepo *self,
+ const char *checksum,
+ GVariant *metadata,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gs_unref_object GFile *metadata_path =
+ _ostree_repo_get_commit_metadata_loose_path (self, checksum);
+
+ if (!g_file_replace_contents (metadata_path,
+ g_variant_get_data (metadata),
+ g_variant_get_size (metadata),
+ NULL, FALSE, 0, NULL,
+ cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
static GVariant *
create_tree_variant_from_hashes (GHashTable *file_checksums,
GHashTable *dir_contents_checksums,
GCancellable *cancellable,
GError **error);
+GFile *
+_ostree_repo_get_commit_metadata_loose_path (OstreeRepo *self,
+ const char *checksum);
+
gboolean
_ostree_repo_has_loose_object (OstreeRepo *self,
const char *checksum,
if (info)
{
+ if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
+ {
+ gs_unref_object GFile *detached_metadata =
+ _ostree_repo_get_commit_metadata_loose_path (data->repo, checksum);
+ if (!ot_gfile_ensure_unlinked (detached_metadata, cancellable, error))
+ goto out;
+ }
if (!gs_file_unlink (objf, cancellable, error))
goto out;
data->freed_bytes += g_file_info_get_size (info);
const char *parent,
const char *subject,
const char *body,
+ GVariant *metadata,
const char *root_contents_checksum,
const char *root_metadata_checksum,
char **out_commit,
GCancellable *cancellable,
GError **error);
+gboolean ostree_repo_read_commit_detached_metadata (OstreeRepo *self,
+ const char *checksum,
+ GVariant **out_metadata,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ostree_repo_write_commit_detached_metadata (OstreeRepo *self,
+ const char *checksum,
+ GVariant *metadata,
+ GCancellable *cancellable,
+ GError **error);
+
/**
* OstreeRepoCheckoutMode:
* @OSTREE_REPO_CHECKOUT_MODE_NONE: No special options
static char *opt_body;
static char *opt_branch;
static char *opt_statoverride_file;
+static char **opt_metadata_strings;
+static char **opt_detached_metadata_strings;
static gboolean opt_link_checkout_speedup;
static gboolean opt_skip_if_unchanged;
static gboolean opt_tar_autocreate_parents;
{ "body", 'm', 0, G_OPTION_ARG_STRING, &opt_body, "Full description", "body" },
{ "branch", 'b', 0, G_OPTION_ARG_STRING, &opt_branch, "Branch", "branch" },
{ "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_trees, "Overlay the given argument as a tree", "NAME" },
+ { "add-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata_strings, "Append given key and value (in string format) to metadata", "KEY=VALUE" },
+ { "add-detached-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_detached_metadata_strings, "Append given key and value (in string format) to detached metadata", "KEY=VALUE" },
{ "owner-uid", 0, 0, G_OPTION_ARG_INT, &opt_owner_uid, "Set file ownership user id", "UID" },
{ "owner-gid", 0, 0, G_OPTION_ARG_INT, &opt_owner_gid, "Set file ownership group id", "GID" },
{ "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Do not import extended attributes", NULL },
return ret;
}
+static gboolean
+parse_keyvalue_strings (char **strings,
+ GVariant **out_metadata,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char **iter;
+ gs_unref_variant_builder GVariantBuilder *builder = NULL;
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+
+ for (iter = strings; *iter; iter++)
+ {
+ const char *s;
+ const char *eq;
+ gs_free char *key = NULL;
+
+ s = *iter;
+
+ eq = strchr (s, '=');
+ if (!eq)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Missing '=' in KEY=VALUE metadata '%s'", s);
+ goto out;
+ }
+
+ key = g_strndup (s, eq - s);
+ g_variant_builder_add (builder, "{sv}", key,
+ g_variant_new_string (eq + 1));
+ }
+
+ ret = TRUE;
+ *out_metadata = g_variant_builder_end (builder);
+ g_variant_ref_sink (*out_metadata);
+ out:
+ return ret;
+}
+
gboolean
ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
{
gs_free char *parent = NULL;
gs_free char *commit_checksum = NULL;
gs_free char *contents_checksum = NULL;
+ gs_unref_variant GVariant *metadata = NULL;
+ gs_unref_variant GVariant *detached_metadata = NULL;
gs_unref_object OstreeMutableTree *mtree = NULL;
gs_free char *tree_type = NULL;
gs_unref_hashtable GHashTable *mode_adds = NULL;
goto out;
}
+ if (opt_metadata_strings)
+ {
+ if (!parse_keyvalue_strings (opt_metadata_strings,
+ &metadata, error))
+ goto out;
+ }
+ if (opt_detached_metadata_strings)
+ {
+ if (!parse_keyvalue_strings (opt_detached_metadata_strings,
+ &detached_metadata, error))
+ goto out;
+ }
+
if (!opt_branch)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
}
if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body,
- contents_checksum, root_metadata,
+ metadata, contents_checksum, root_metadata,
&commit_checksum, cancellable, error))
goto out;
+ if (detached_metadata)
+ {
+ if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum,
+ detached_metadata,
+ cancellable, error))
+ goto out;
+ }
+
ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum);
if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error))
static gboolean opt_print_related;
static char* opt_print_variant_type;
static char* opt_print_metadata_key;
+static char* opt_print_detached_metadata_key;
static gboolean opt_raw;
static GOptionEntry options[] = {
{ "print-related", 0, 0, G_OPTION_ARG_NONE, &opt_print_related, "If given, show the \"related\" commits", NULL },
{ "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &opt_print_variant_type, "If given, argument should be a filename and it will be interpreted as this type", NULL },
{ "print-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_metadata_key, "Print string value of metadata key KEY for given commit", "KEY" },
+ { "print-detached-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_detached_metadata_key, "Print string value of detached metadata key KEY for given commit", "KEY" },
{ "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "Show raw variant data" },
{ NULL }
};
}
static gboolean
-do_print_metadata_key (OstreeRepo *repo,
- const char *resolved_rev,
- const char *key,
- GError **error)
+do_print_metadata_key (OstreeRepo *repo,
+ const char *resolved_rev,
+ gboolean detached,
+ const char *key,
+ GError **error)
{
gboolean ret = FALSE;
const char *value;
gs_unref_variant GVariant *commit = NULL;
gs_unref_variant GVariant *metadata = NULL;
- if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
- resolved_rev, &commit, error))
- goto out;
-
- /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
- metadata = g_variant_get_child_value (commit, 1);
+ if (!detached)
+ {
+ if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
+ resolved_rev, &commit, error))
+ goto out;
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ metadata = g_variant_get_child_value (commit, 0);
+ }
+ else
+ {
+ if (!ostree_repo_read_commit_detached_metadata (repo, resolved_rev, &metadata,
+ NULL, error))
+ goto out;
+ }
if (!g_variant_lookup (metadata, key, "&s", &value))
goto out;
return ret;
}
-
static gboolean
print_object (OstreeRepo *repo,
OstreeObjectType objtype,
}
rev = argv[1];
- if (opt_print_metadata_key)
+ if (opt_print_metadata_key || opt_print_detached_metadata_key)
{
+ gboolean detached = opt_print_detached_metadata_key != NULL;
+ const char *key = detached ? opt_print_detached_metadata_key : opt_print_metadata_key;
if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error))
goto out;
- if (!do_print_metadata_key (repo, resolved_rev, opt_print_metadata_key, error))
+ if (!do_print_metadata_key (repo, resolved_rev, detached, key, error))
goto out;
}
else if (opt_print_related)
set -e
-echo "1..40"
+echo "1..41"
. $(dirname $0)/libtest.sh
touch checkout-test2/sometestfile
$OSTREE commit -s sometest -b test2 checkout-test2
echo "ok commit with directory filename"
+
+$OSTREE commit -b test2 -s "Metadata string" --add-metadata-string=FOO=BAR --add-metadata-string=KITTENS=CUTE --add-detached-metadata-string=SIGNATURE=HANCOCK --tree=ref=test2
+$OSTREE show --print-metadata-key=FOO test2 > test2-meta
+assert_file_has_content test2-meta "BAR"
+$OSTREE show --print-metadata-key=KITTENS test2 > test2-meta
+assert_file_has_content test2-meta "CUTE"
+$OSTREE show --print-detached-metadata-key=SIGNATURE test2 > test2-meta
+assert_file_has_content test2-meta "HANCOCK"
+echo "ok metadata commit with strings"